﻿using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Globalization; // DateTime.TryParseExactに使用
using System.IO;
using System.Linq;
using System.Management; //VSで参照追加必要
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;




/*
ObservePC バージョン履歴

2025.10.16 Ver1.0
- Windows 10/11 + .NET Framework4.7.2 対応のコンソールツール
- WMIを用いてPC環境情報を取得
- AIとの対話時に前提整形として活用
- GUI不要、コピーペースト最適化
- 改変・配布自由（不正改変は禁止）

2025.10.17 Ver1.1
- user_note.txt の連結機能を追加
- 起動オプションによる属人属性の明示（-L, -H, -M）
- モニター情報取得機能を追加

2025.10.21 Ver1.2
- 以下の機能を追加：
  ・スタートアップ一覧
  ・Windows Update履歴
  ・NIC構成
  ・常駐プロセス一覧

2025.10.22 Ver1.3
- 以下の修正
常駐プロセス一覧がアルファベット順に出てるだけ。これでは意味をなさない
メソッドを書き換えて、サードパーティ製プロセス一覧（常駐候補）へ切り替え

2025.10.24 Ver1.4
- 以下の修正
ターゲットフレームワーク4.7.2から4.8へ変更
取得情報の強化。BIOS情報、NIC情報拡張、OSインストール日、セキュリティ状況(セキュリティソフトの推測)、タスクスケジューラ登録
管理・教材用途を考慮してLAN出力（SMB/HTTP/Syslog/SMTP）のコメントアウト＋テンプレート追加

2025.11.7 Ver1.6
Ver1.5での失敗を踏まえて、互換性の確保に注力 PowerShell依存を完全撤廃
ターゲットフレームワーク4.7.2へ再び変更。Server2019で動作確認
サービスの出力を追加。JSONはLAN出力同様にコメントアウト
サービス一覧（異常・停止・無効)を追加

2025.11.7 Ver1.65
ファイアウォール通過許可ルール（残骸検出）をユーザー/アプリが設定したファイアウォールルールへ変更

2025.11.12 Ver1.66
タスクスケジューラーとサービス情報を改修

 2025.11.7 Ver1.67
 情報取得を並列実行へ変更 高速モード-F新設

 2025.11.16 Ver1.68
 並列処理の補完として逐次実行を追加 安全モード-S新設
 モード指定無し・-Eモード時に取得確認と補完追加

 2025.11.17 Ver1.69
 -Sモードで逐次取得を2回るように改良
 */




// Win32 API 関数と構造体の定義
class Program
{
    //グローバル(クラス内)変数の宣言
    static ConcurrentDictionary<int, string> sb_data = new ConcurrentDictionary<int, string>(); //メソッドからの値収納用
    static Dictionary<int, string> sb_Comment = new Dictionary<int, string>(); //sbのインデクス
    static Dictionary<int, string> sb_Method_Name = new Dictionary<int, string>(); //sbの取得メソッド

    // ディスプレイ情報を取得するためのWin32 API構造体（DISPLAY_DEVICE）
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct DISPLAY_DEVICE
    {
        public int cb; // 構造体のサイズ (Marshal.SizeOf(DISPLAY_DEVICE)で初期化)
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DeviceName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceString; // デバイスのユーザーフレンドリーな名前
        public int StateFlags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceKey;
    }

    // user32.dllからEnumDisplayDevices関数をインポート
    // これにより、Win32 APIのネイティブな機能（GPU/モニター情報取得）をC#から呼び出せる
    [DllImport("user32.dll", CharSet = CharSet.Ansi)]
    public static extern bool EnumDisplayDevices(
        string lpDevice,
        uint iDevNum,
        ref DISPLAY_DEVICE lpDisplayDevice,
        uint dwFlags
    );

    // インストール済みアプリ情報を格納するシンプルなクラス
    class InstalledApp
    {
        public string Name { get; set; }
        public DateTime? InstallDate { get; set; } // インストール日 (取得できない場合もあるためNullable)
    }


    // プログラムのエントリポイント（メイン処理）

    static void Main(string[] args)
    {
        DateTime dt1 = DateTime.Now;    //時刻取得
        Console.WriteLine(dt1);
        for (int i = 1; i <= 26; i++)
        {
            sb_data[i] = "";
        }
        //-Eモード検出
        bool expertMode = args.Contains("-E", StringComparer.OrdinalIgnoreCase);
        byte MaxDataOutput = 10; // 通常モードは10件、-Eで30件
        foreach (string arg in args)
        {
            if (arg.Equals("-E", StringComparison.OrdinalIgnoreCase))
            {
                MaxDataOutput = 30;
            }
        }

        //-Fモード検出
        bool FastMode = args.Contains("-F", StringComparer.OrdinalIgnoreCase);
        foreach (string arg in args)
        {
            if (arg.Equals("-F", StringComparison.OrdinalIgnoreCase))
            {
                expertMode = true;
                MaxDataOutput = 30;
            }
        }

        //-Sモード検出
        bool SafetyMode = args.Contains("-S", StringComparer.OrdinalIgnoreCase);
        foreach (string arg in args)
        {
            if (arg.Equals("-S", StringComparison.OrdinalIgnoreCase))
            {
                expertMode = true;
                MaxDataOutput = 30;
            }
        }

        // StringBuilderは、文字列を効率よく結合するためのクラス
        var sb = new StringBuilder(8192);
        string ver = "ObservePC Ver1.7";
        ver = ver + " :" + dt1 + " :" + TimeZoneInfo.Local;
        sb.Append(ver + " :");
        //sb.AppendLine(GetLicenseStatus()); 
        TimeZoneInfo tz = TimeZoneInfo.Local;
        //string timeZoneName = tz.DisplayName;
        //sb.Append(dt1 + " :");
        //sb.AppendLine();
        //sb.AppendLine(GetNtpSource());      
        //sb.AppendLine();
        //sb_data[0] = ver;
        //sb_Commentとsb_Method_Nameに値を入れる
        InitializeMethodMaps();

        string[] sectionHeaders = new[]
        {
    "?? OS情報：",
    "?? マザーボード情報：",
    "?? CPUとメモリー情報：",
    "?? ストレージ情報 (容量と空き容量)：",
    "?? 接続されているデバイス一覧（分類・最大10件）：",
    "?? GPU情報：",
    "?? ディスプレイ情報（EnumDisplayDevicesより）：",
    "?? ネットワーク構成（System.Net + WMIより）：",
    "?? 環境変数 (Path, Tempなど)：",
    "?? Windows Update履歴（KB番号とインストール日）：",
    "?? 最近インストールされたソフトウェア（最大10件）：",
    "?? スタートアップアプリ一覧：",
    "?? サードパーティ製プロセス一覧（常駐候補）：",
    "?? セキュリティソフトの常駐状況（推定）：",
    "?? ファイアウォール概要",
    "?? 異常ドライバー検出:",
    "?? 最近のシステムイベントログ（Error/Warningのみ、10016除外 最大10件）：",
    "?? ユーザーアカウント一覧：",
    "?? タスクスケジューラ登録一覧（整形済・schtasks.exeより）：",
    "?? 削除ソフト痕跡（レジストリより推定）：",
    "?? 削除ソフト痕跡（イベントログより確定）：",
    "?? サービス一覧（異常・停止・無効)：",
    "?? ユーザー/アプリが設定したファイアウォールルール:"
};

        //並列取得
        if (!SafetyMode)
        {
            var result = GetAllSystemDataAsync(MaxDataOutput, expertMode, FastMode).GetAwaiter().GetResult();
            sb.Append(result);
        }

        //逐次取得、FastMode除く
        if (!FastMode)
        {
            byte i = 0;
            do
            {
                GetlSequential(MaxDataOutput, expertMode);
                i++;
                //Console.WriteLine(i);
            } while ((i < 2)&&(SafetyMode));    //SafetyModeだけ2回取得

        }

        //連結
        // すべてのsb_dataを連結（空欄はスキップ）
        //sb.AppendLine("==== 統合出力 ====");
        int MaxIndex = 27;
        for (int i = 0; i < MaxIndex; i++) // MaxIndex は 27 など、事前に定義
        {
            if (sb_data.TryGetValue(i, out string value) && !string.IsNullOrWhiteSpace(value))
            {
                string trimmed = value.TrimEnd(); // ← 末尾の改行を除去
                sb.AppendLine(trimmed);
                sb.AppendLine(); // ← 1行だけ追加
            }

        }

        //ユーザー補足情報ファイル (user_note.txt) の読み込み
        string noteFile = "user_note.txt";
        if (System.IO.File.Exists(noteFile))
        {
            sb.AppendLine("== 使用者補足情報（user_note.txt より） ==");
            // UTF8で読み込み (日本語文字化け対策)
            string[] notes = System.IO.File.ReadAllLines(noteFile, Encoding.UTF8);
            foreach (string line in notes)
            {
                sb.AppendLine(line);
            }
        }

        //起動オプションによる属人属性（AI応答プロトコルヘッダー）の追加
        List<string> userNotes = new List<string>();

        foreach (string arg in args)
        {
            // -L: Learner (初心者)
            if (arg.Equals("-L", StringComparison.OrdinalIgnoreCase))
            {
                userNotes.Add("--- [AI応答プロトコルヘッダー] ---");
                userNotes.Add("説明要求レベル: 初心者 (-L)");
                userNotes.Add("AIへの説明要求: 初心者です。専門用語を避け、平易に説明してください。");
            }
            // -H: Highly experienced (経験者)
            else if (arg.Equals("-H", StringComparison.OrdinalIgnoreCase))
            {
                userNotes.Add("--- [AI応答プロトコルヘッダー] ---");
                userNotes.Add("説明要求レベル: 経験者 (-H)");
                userNotes.Add("AIへの説明要求: 経験者です。簡潔に、要点だけを伝えてください。");
            }
            // -M=message: カスタムメッセージ
            else if (arg.StartsWith("-M=", StringComparison.OrdinalIgnoreCase) && arg.Length > 3)
            {
                string rawMessage = arg.Substring(3);
                // 引用符(' " ')やスペースをトリムしてメッセージを整形
                string message = rawMessage.Trim('"', '\'', ' ');

                if (!string.IsNullOrEmpty(message))
                {
                    userNotes.Add("--- [AI応答プロトコルヘッダー] ---");
                    userNotes.Add("説明要求レベル: 標準 (-M) + カスタム");
                    userNotes.Add($"AIへの説明要求: {message}");
                }
            }
        }

        if (userNotes.Count > 0)
        {
            sb.AppendLine();
            sb.AppendLine("== 起動オプションによる使用者属性 ==");
            foreach (string line in userNotes)
            {
                sb.AppendLine(line);
            }
        }


        // == 相談開始宣言 ==
        sb.AppendLine("以上の環境で相談があります");

        //モード確認
        //sb.Append("expertMode :");
        //sb.Append(expertMode.ToString());
        //sb.Append(" / FastMode :");
        //sb.AppendLine(FastMode.ToString());

        string machine = Environment.MachineName;
        string date = DateTime.Now.ToString("yyyyMMdd");

        string finalOutput = Regex.Replace(sb.ToString(), @"\x00|\x01|\x02|\x03|\x04|\x05|\x06|\x07|\x08|\x0B|\x0C|\x0E-\x1F", "");

        string txtFile = $"PC_Observation_{machine}_{date}.txt";
        string jsonFile = $"PC_Observation_{machine}_{date}.json";

        var snapshot = new Dictionary<string, string>();
        string sbText = finalOutput; // sb.ToString() 後の整形済みテキスト

        foreach (string header in sectionHeaders)
        {
            string key = header.Trim('?', '：').Trim();
            string sectionText = ExtractSection(sbText, header);
            snapshot[key] = sectionText;
        }



        // 並列出力処理（Task.Runベース）
        PerformOutput(sbText, snapshot, txtFile, jsonFile);
        /*
                var outputTasks = new List<Task>
        {
            Task.Run(() => Console.Write(sbText)),
            Task.Run(() => File.WriteAllText(txtFile, sbText,new UTF8Encoding(false))),
            Task.Run(() => {
            try
            {
                var json = Newtonsoft.Json.JsonConvert.SerializeObject(snapshot, Newtonsoft.Json.Formatting.Indented);
                File.WriteAllText(jsonFile, json, Encoding.UTF8);
                Console.WriteLine($"JSON出力完了: {jsonFile}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"JSON出力失敗: {ex.Message}");
            }
        })


        };

                try
                {
                    Task.WaitAll(outputTasks.ToArray());
                }
                catch (Exception ex)
                {
                    Console.WriteLine("出力処理中にエラーが発生しました: " + ex.Message);
                }
        */

        // == 転送テンプレート（必要に応じて有効化） ==
        // 必要に応じて、以下の関数を有効化・改変してください

        // SMB共有フォルダへコピー
        // OutputSender.SendToSharedFolder(fileName);

        // HTTP POST送信
        // OutputSender.SendToHttpServer(fileName);

        // FTPアップロード
        // OutputSender.SendToFtpServer(fileName);

        // Syslog送信（UDP 514）
        // OutputSender.SendToSyslog(fileName);

        // SMTP送信（LAN内・ポート25・認証なし）
        // OutputSender.SendViaSmtp_LAN(fileName);



        // 終了待機 LAN出力転送するならコメントアウト推奨
        DateTime dt2 = DateTime.Now;    //時刻取得
        Console.WriteLine(dt2);
        TimeSpan timeSpan = dt2 - dt1;
        Console.Write("処理時間 :");
        Console.WriteLine(timeSpan);
        Console.WriteLine("完了しました。何かキーを押すと終了します。");
        Console.ReadKey();
    }



    //出力メソッド
    static void PerformOutput(string sbText, Dictionary<string, string> snapshot, string txtFile, string jsonFile)
    {
        // JSON出力先の判定（FAT32なら一時フォルダへ退避）
        string jsonFullPath = Path.GetFullPath(jsonFile);
        string jsonTarget = jsonFullPath;

        try
        {
            DriveInfo drive = new DriveInfo(Path.GetPathRoot(jsonFullPath));
            if (drive.DriveFormat.Equals("FAT32", StringComparison.OrdinalIgnoreCase))
            {
                Console.WriteLine("FAT32環境のため、JSON出力先を一時フォルダに変更します。");
                jsonTarget = Path.Combine(Path.GetTempPath(), Path.GetFileName(jsonFile));
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ドライブ判定に失敗しました（相対パスの可能性）: " + ex.Message);
            // jsonTarget はそのまま jsonFullPath を使う
        }

        // 並列出力処理
        var outputTasks = new List<Task>
        {
        Task.Run(() => Console.Write(sbText)),
        Task.Run(() => File.WriteAllText(txtFile, sbText, new UTF8Encoding(false))),
        // JSON出力（Ver1.6ではコメントアウト）

        /*
        Task.Run(() => {
            try
            {
                var cleanSnapshot = snapshot.ToDictionary(
                    kv => kv.Key,
                    kv => Regex.Replace(kv.Value ?? "", @"[\x00-\x1F]", "")
                );

                var json = Newtonsoft.Json.JsonConvert.SerializeObject(cleanSnapshot, Newtonsoft.Json.Formatting.Indented);
                File.WriteAllText(jsonTarget, json, new UTF8Encoding(false));
                Console.WriteLine($"JSON出力完了: {jsonTarget}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"JSON出力失敗: {ex.GetType().Name} - {ex.Message}");
            }
        })
        */
        };

        try
        {
            Task.WaitAll(outputTasks.ToArray());
        }
        catch (AggregateException ae)
        {
            foreach (var ex in ae.InnerExceptions)
            {
                Console.WriteLine("出力タスク例外: " + ex.GetType().Name + " - " + ex.Message);
            }
        }
    }

    //辞書の初期値入力
    static void InitializeMethodMaps()
    {
        var mappings = new (int Index, string Comment, string Method)[]
        {
        (0, "ヘッダー", "Main"),
        (1, "PC起動時刻", "OutputUptimeInfo"),
        (2, "コンピュータ名、ドメイン、ユーザー", "OutputMachineInfo"),
        (3, "OS情報", "OutputOSInfo"),
        (4, "マザーボード情報", "OutputMotherboardInfo"),
        (5, "BIOS情報", "OutputBIOSInfo"),
        (6, "CPUとメモリー情報", "OutputSystemInfo"),
        (7, "ストレージ情報 (容量と空き容量)", "OutputDiskInfo"),
        (8, "GPU情報", "OutputGPUInfo"),
        (9, "ディスプレイ情報（EnumDisplayDevicesより）", "OutputDisplayInfo"),
        (10, "ネットワーク構成（System.Net + WMIより）", "OutputNetworkInfo"),
        (11, "接続されているデバイス一覧（分類・最大10件）", "OutputDeviceInfo"),
        (12, "環境変数（ユーザー・システム環境変数）", "OutputEnvironmentVariables"),
        (13, "Windows Update履歴（KB番号とインストール日）", "OutputUpdateHistory"),
        (14, "最近インストールされたソフトウェア", "OutputSoftwareInfo"),
        (15, "スタートアップアプリ一覧", "OutputStartupApps"),
        (16, "サードパーティ製プロセス一覧（常駐候補）", "OutputResidentProcesses"),
        (17, "セキュリティソフトの常駐状況（推定）", "OutputSecurityStatus"),
        (18, "ファイアウォールプロファイル状態", "GetFirewallProfileStatus"),
        (19, "異常ドライバー検出（未署名・不明ドライバー）", "OutputDriverIssues"),
        (20, "最近のシステムイベントログ（Error/Warningのみ）", "OutputEventLog"),

        // === 以下 -E モード専用 ===
        (21, "ユーザーアカウント一覧", "OutputUserAccounts"),
        (22, "タスクスケジューラ登録一覧（整形済・フィルタ＋状態＋トリガー型対応）", "GetTaskSchedulerInfo"),
        (23, "削除ソフト痕跡（レジストリ推定/イベントログ確定）", "OutputUninstallTraces"),
        (24, "23と統合につき空白", ""),
        (25, "サービス一覧（異常・停止・無効）", "GetFilteredServices"),
        (26, "ユーザー/アプリが設定したファイアウォールルール", "OutputFirewallRules_E")        };

        foreach (var m in mappings)
        {
            sb_Comment[m.Index] = m.Comment;
            sb_Method_Name[m.Index] = m.Method;
        }
    }

    // --- 各情報取得メソッド ---

    // Win32 API (EnumDisplayDevices) を使用してGPUと接続モニター情報を取得
    static string OutputDisplayInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ディスプレイ情報（EnumDisplayDevicesより）：");

        try
        {
            DISPLAY_DEVICE gpu = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };

            for (uint i = 0; EnumDisplayDevices(null, i, ref gpu, 0); i++)
            {
                sb.AppendLine($"GPU {i}: {gpu.DeviceString}");

                DISPLAY_DEVICE monitor = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };

                if (EnumDisplayDevices(gpu.DeviceName, 0, ref monitor, 0))
                {
                    sb.AppendLine($"  -> モニター: {monitor.DeviceString}");
                    sb.AppendLine($"     PnP名: {monitor.DeviceName}");
                    break;
                }

                gpu = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputDisplayInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }

    // WMI (Win32_OperatingSystem) からOS情報を取得

    // OS情報
    static string OutputOSInfo()
    {
        var sb = new StringBuilder();
        // 共通ヘッダー
        sb.AppendLine("--- [PC環境リスト] ---");
        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT Caption, Version, OSArchitecture, CSDVersion FROM Win32_OperatingSystem"))
            {
                foreach (var os in searcher.Get())
                {
                    sb.AppendLine($"[OS] {os["Caption"]} {os["Version"]} ({os["OSArchitecture"]}) {os["CSDVersion"]}");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OS情報取得失敗] {ex.Message}");
        }
        return sb.ToString();
    }


    // WMI (Win32_Processor, Win32_ComputerSystem, Win32_PhysicalMemory) からCPU/RAM情報を取得

    static string OutputSystemInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? CPUとメモリー情報：");

        // CPU情報
        var cpuSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_Processor");
        foreach (ManagementObject cpu in cpuSearcher.Get())
        {
            sb.AppendLine($"CPU: {cpu["Name"]}");
            sb.AppendLine($"コア数: {cpu["NumberOfCores"]}, スレッド数: {cpu["NumberOfLogicalProcessors"]}");
        }

        // RAM合計容量
        var memSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem");
        foreach (ManagementObject mem in memSearcher.Get())
        {
            // TotalPhysicalMemory はバイト単位なので、MBに変換
            ulong totalMemory = Convert.ToUInt64(mem["TotalPhysicalMemory"]);
            sb.AppendLine($"RAM合計: {totalMemory / (1024 * 1024)} MB");
        }

        // RAM速度と種類 (スロット別)
        var ramSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMemory");
        foreach (ManagementObject ram in ramSearcher.Get())
        {
            // MemoryType (24=DDR3, 26=DDR4, 34=DDR5 など。WMIの規格値を使用)
            string memoryType = GetMemoryType(Convert.ToInt32(ram["MemoryType"]));
            ulong sizeMB = Convert.ToUInt64(ram["Capacity"]) / (1024 * 1024);
            sb.AppendLine($"RAM Slot: {ram["DeviceLocator"]}, Speed: {ram["Speed"]} MHz, Type: {memoryType}, Size: {sizeMB} MB");
        }
        return sb.ToString();
    }

    // WMIのMemoryTypeコードをDDR世代に変換 (C# 8.0以降のswitch式を使用 以前でも.csprojに追記で可能な場合有り)

    static string GetMemoryType(int type)
    {
        return type switch
        {
            24 => "DDR3",
            26 => "DDR4",
            34 => "DDR5",
            _ => "Unknown" // その他の値はUnknownとする
        };
    }


    // WMI (Win32_LogicalDisk) からストレージ情報を取得

    static string OutputDiskInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ストレージ情報 (容量と空き容量)：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_LogicalDisk");
            foreach (ManagementObject disk in searcher.Get())
            {
                string deviceId = Clean(disk["DeviceID"]?.ToString()).ToUpper();
                string volumeName = Clean(disk["VolumeName"]?.ToString());

                int driveType = Convert.ToInt32(disk["DriveType"]);
                string typeLabel = driveType switch
                {
                    2 => "リムーバブル",
                    3 => "ローカル",
                    4 => "ネットワーク",
                    5 => "CD/DVD",
                    6 => "RAMディスク",
                    _ => "その他"
                };

                ulong totalBytes = disk["Size"] != null ? Convert.ToUInt64(disk["Size"]) : 0;
                ulong freeBytes = disk["FreeSpace"] != null ? Convert.ToUInt64(disk["FreeSpace"]) : 0;

                string totalGB = (totalBytes / (1024 * 1024 * 1024)).ToString();
                string freeGB = (freeBytes / (1024 * 1024 * 1024)).ToString();

                sb.AppendLine($"ドライブ {deviceId} - 合計: {totalGB} GB, 空き: {freeGB} GB, ボリューム名: {volumeName}, 種別: {typeLabel}");
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputDiskInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }


    static string Clean(string input) => input?.Replace("\0", "").Trim() ?? "";



    //接続デバイス　主にUSB
    static string OutputDeviceInfo(byte Max)
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? 接続されているデバイス一覧（分類・最大" + Max + "件）：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity");
            var allDevices = new List<string>();
            var avDevices = new List<string>();

            foreach (ManagementObject device in searcher.Get())
            {
                string name = device["Name"]?.ToString();
                if (string.IsNullOrEmpty(name)) continue;

                if (name.IndexOf("Bus", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Component", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Driver", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Service", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Controller", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    continue;
                }

                if (name.IndexOf("Microphone", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Webcam", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Camera", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Audio", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("DAC", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Capture", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Video", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    avDevices.Add(name);
                }
                else if (
                    name.IndexOf("Hub", StringComparison.OrdinalIgnoreCase) < 0 &&
                    (name.IndexOf("USB", StringComparison.OrdinalIgnoreCase) >= 0 ||
                     name.IndexOf("Bluetooth", StringComparison.OrdinalIgnoreCase) >= 0 ||
                     name.IndexOf("Printer", StringComparison.OrdinalIgnoreCase) >= 0 ||
                     name.IndexOf("Display", StringComparison.OrdinalIgnoreCase) >= 0))
                {
                    bool isHub =
                        name.IndexOf("Hub", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.Contains("ハブ") ||
                        name.Contains("ルート ハブ") ||
                        name.Contains("Composite") ||
                        name.Contains("SuperSpeed USB ハブ");

                    bool isUsefulUSB =
                        name.IndexOf("Input", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Keyboard", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Mouse", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Storage", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Logitech", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("DAC", StringComparison.OrdinalIgnoreCase) >= 0;

                    if (!isHub && (name.IndexOf("USB", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   name.IndexOf("Bluetooth", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   name.IndexOf("Printer", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   name.IndexOf("Display", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   isUsefulUSB))
                    {
                        allDevices.Add(name);
                    }
                }
            }

            sb.AppendLine("?? 録音・映像機器:");
            foreach (var dev in avDevices.Take(Max))
            {
                sb.AppendLine("- " + dev);
            }

            sb.AppendLine("??? その他の接続デバイス:");
            foreach (var dev in allDevices.Take(Max))
            {
                sb.AppendLine("- " + dev);
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputDeviceInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }


    // WMI (Win32_VideoController) からGPU情報を取得

    static string OutputGPUInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? GPU情報：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_VideoController");

            foreach (ManagementObject gpu in searcher.Get())
            {
                sb.AppendLine($"名前: {gpu["Name"]}");
                sb.AppendLine($"ドライババージョン: {gpu["DriverVersion"]}");

                // AdapterRAM (VRAM) は4GB以上でWMIの型変換に問題が出ることがあるため、チェックを追加
                if (gpu["AdapterRAM"] is ulong adapterRam)
                {
                    sb.AppendLine($"VRAM: {adapterRam / (1024 * 1024)} MB");
                }
                else
                {
                    sb.AppendLine("VRAM: (取得不可 or 4GB超過によるWMIの制限) MB");
                }

                // 現在の解像度情報
                if (gpu["CurrentHorizontalResolution"] != null && gpu["CurrentVerticalResolution"] != null)
                {
                    sb.AppendLine($"解像度: {gpu["CurrentHorizontalResolution"]} x {gpu["CurrentVerticalResolution"]}");
                }

                sb.AppendLine();
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputGPUInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }

    // Windowsイベントログから最近のエラーと警告を取得し、ノイズを除去

    static string OutputEventLog(byte Max)
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? 最近のシステムイベントログ（Error/Warningのみ、10016除外 最大" + Max + "件）:");

        try
        {
            using (EventLog systemLog = new EventLog("System"))
            {
                int count = 0;
                for (int i = systemLog.Entries.Count - 1; i >= 0 && count < Max; i--)
                {
                    EventLogEntry entry = systemLog.Entries[i];

                    if ((entry.EntryType == EventLogEntryType.Error || entry.EntryType == EventLogEntryType.Warning)
                        && entry.Source != "DCOM" && entry.EventID != 10016)
                    {
                        sb.AppendLine($"[{entry.TimeGenerated:yyyy-MM-dd HH:mm:ss}] {entry.EntryType} - Source: {entry.Source}, EventID: {entry.EventID}");

                        string message = entry.Message.Replace('\n', ' ').Replace('\r', ' ');
                        sb.AppendLine($"    -> Message: {message.Substring(0, Math.Min(message.Length, 150))}...");

                        count++;
                    }
                }

                if (count == 0)
                {
                    sb.AppendLine("-> 過去のログに重大なError/Warningは検出されませんでした。");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputEventLog] エラー: {ex.Message}");
        }

        return sb.ToString();
    }

    // WMI (Win32_BaseBoard) からマザーボード情報を取得

    // マザーボード情報
    static string OutputMotherboardInfo()
    {
        var sb = new StringBuilder();
        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT Manufacturer, Product, SerialNumber FROM Win32_BaseBoard"))
            {
                foreach (var board in searcher.Get())
                {
                    sb.AppendLine($"[マザーボード] {board["Manufacturer"]} {board["Product"]} SN:{board["SerialNumber"]}");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[マザーボード情報取得失敗] {ex.Message}");
        }
        return sb.ToString();
    }


    // WMI (Win32_OperatingSystem) からPCの起動時刻と稼働時間を取得

    static string OutputUptimeInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine();
        sb.AppendLine("?? 時間情報：");

        var searcher = new ManagementObjectSearcher("SELECT LastBootUpTime FROM Win32_OperatingSystem");
        foreach (ManagementObject os in searcher.Get())
        {
            string lastBootUp = os["LastBootUpTime"]?.ToString();
            if (!string.IsNullOrEmpty(lastBootUp))
            {
                // WMIの日付形式をC#のDateTimeに変換
                DateTime bootTime = ManagementDateTimeConverter.ToDateTime(lastBootUp);
                // TimeSpan uptime = DateTime.Now - bootTime; // 稼働時間は省略 (コメントアウト)

                sb.AppendLine($"PC起動時刻: {bootTime:yyyy-MM-dd HH:mm:ss}");
            }
        }
        return sb.ToString();
    }


    // 環境変数からPC名を取得

    // コンピュータ名、ドメイン、ユーザーなど
    static string OutputMachineInfo()
    {
        var sb = new StringBuilder();
        try
        {
            sb.AppendLine($"[マシン名] {Environment.MachineName}");
            sb.AppendLine($"[ユーザー名] {Environment.UserName}");
            sb.AppendLine($"[ドメイン] {Environment.UserDomainName}");
            sb.AppendLine($"[アーキテクチャ] {(Environment.Is64BitOperatingSystem ? "64bit" : "32bit")}");
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[マシン情報取得失敗] {ex.Message}");
        }
        return sb.ToString();
    }

    //スタートアップ一覧取得
    static string OutputStartupApps()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? スタートアップアプリ一覧：");

        try
        {
            string registryPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(registryPath))
            {
                if (key != null)
                {
                    foreach (string name in key.GetValueNames())
                    {
                        string value = key.GetValue(name)?.ToString();
                        sb.AppendLine($"- {name}: {value}");
                    }
                }
                else
                {
                    sb.AppendLine("-> スタートアップ情報が取得できませんでした。");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputStartupApps] エラー: {ex.Message}");
        }

        return sb.ToString();
    }



    //Windows Update履歴取得
    static string OutputUpdateHistory()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? Windows Update履歴（KB番号とインストール日）：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_QuickFixEngineering");
            foreach (ManagementObject update in searcher.Get())
            {
                string hotfixId = update["HotFixID"]?.ToString();
                string installedOn = update["InstalledOn"]?.ToString();
                sb.AppendLine($"- {hotfixId} (Installed: {installedOn})");
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputUpdateHistory] エラー: {ex.Message}");
        }

        return sb.ToString();
    }

    // NIC構成取得（Ver1.4+ 製造元付き・MAC照合方式）
    static string OutputNetworkInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ネットワーク構成（System.Net + WMIより）：");

        // WMIから MAC → 製造元 の辞書を作成
        var vendorMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        try
        {
            var wmiSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapter WHERE PhysicalAdapter = TRUE");
            foreach (ManagementObject nic in wmiSearcher.Get())
            {
                string mac = nic["MACAddress"]?.ToString();
                string manufacturer = nic["Manufacturer"]?.ToString();
                if (!string.IsNullOrEmpty(mac) && !string.IsNullOrEmpty(manufacturer))
                {
                    string normalizedMac = mac.Replace(":", "").Replace("-", "").ToUpperInvariant();
                    vendorMap[normalizedMac] = manufacturer;
                }
            }
        }
        catch
        {
            sb.AppendLine("-> NIC製造元情報の取得に失敗しました（WMIアクセス不可）");
        }

        // System.Net.NetworkInformation でNIC状態を取得
        var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();

        foreach (var ni in interfaces)
        {
            if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
            var props = ni.GetIPProperties();
            var ipList = props.UnicastAddresses.Select(ip => ip.Address.ToString()).ToList();
            var dnsList = props.DnsAddresses.Select(dns => dns.ToString()).ToList();
            var gatewayList = props.GatewayAddresses.Select(g => g.Address.ToString()).ToList();

            string mac = ni.GetPhysicalAddress().ToString().ToUpperInvariant();

            sb.AppendLine($"NIC: {ni.Name}");

            if (!string.IsNullOrEmpty(mac) && vendorMap.TryGetValue(mac, out string vendor))
            {
                sb.AppendLine($"  製造元: {vendor}");
            }
            else
            {
                sb.AppendLine($"  製造元: (取得不可 or WMI照合失敗)");
            }

            sb.AppendLine($"  種別: {ni.NetworkInterfaceType}");
            sb.AppendLine($"  状態: {ni.OperationalStatus}");
            sb.AppendLine($"  MAC: {mac}");
            sb.AppendLine($"  リンク速度: {ni.Speed / 1_000_000} Mbps");
            sb.AppendLine($"  IP: {string.Join(", ", ipList)}");
            sb.AppendLine($"  DNS: {string.Join(", ", dnsList)}");
            sb.AppendLine($"  Gateway: {string.Join(", ", gatewayList)}");
        }
        return sb.ToString();
    }


    static string OutputResidentProcesses()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? サードパーティ製プロセス一覧（常駐候補）：");

        var windowsPaths = new[]
        {
           "C:\\Windows",
           "C:\\Program Files\\WindowsApps",
           "C:\\Program Files\\Microsoft Visual Studio",
           //"C:\\Users\\<USERNAME>\\source\\repos", // 自作実行ファイル
           //"C:\\Users\\<USERNAME>\\AppData\\Local\\Microsoft", // BingSvcなど
           // 必要に応じて追加
        };
        var processes = Process.GetProcesses();

        // グループ化用の辞書：フォルダパス → プロセス一覧
        var groups = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);

        foreach (var proc in processes)
        {
            try
            {
                string name = proc.ProcessName;
                string path = proc.MainModule?.FileName ?? null;

                if (string.IsNullOrEmpty(path)) continue;

                // Windows標準プロセスを除外
                if (windowsPaths.Any(p => path.StartsWith(p, StringComparison.OrdinalIgnoreCase)))
                    continue;

                string folder = Path.GetDirectoryName(path) ?? "(不明なフォルダ)";
                string entry = $"- {name}: {path}";

                if (!groups.ContainsKey(folder))
                    groups[folder] = new List<string>();

                groups[folder].Add(entry);
            }
            catch
            {
                // アクセス拒否などは無視
            }
        }

        // 出力（フォルダ単位でグループ化）
        foreach (var kv in groups.OrderBy(k => k.Key))
        {
            sb.AppendLine($"\n■ {kv.Key}：");
            foreach (var line in kv.Value)
            {
                sb.AppendLine(line);
            }
        }
        return sb.ToString();
    }
    //セキュリティソフトの常駐状況
    static string OutputSecurityStatus()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? セキュリティソフトの常駐状況（推定）：");

        try
        {
            var searcher = new ManagementObjectSearcher(@"root\SecurityCenter2", "SELECT * FROM AntiVirusProduct");
            foreach (ManagementObject av in searcher.Get())
            {
                sb.AppendLine($"- 製品名: {av["displayName"]}");
                sb.AppendLine($"  状態: {av["productState"]}"); // 数値コード（詳細はMS仕様）
            }
        }
        catch
        {
            sb.AppendLine("-> セキュリティセンター情報が取得できませんでした。");
        }

        // サードパーティ製の推定（プロセス名から）
        var knownAV = new[] { "eset", "avast", "norton", "mcafee", "kaspersky", "trend", "bitdefender" };
        var processes = Process.GetProcesses();

        var detected = processes
            .Where(p => knownAV.Any(av => p.ProcessName.ToLower().Contains(av)))
            .Select(p => p.ProcessName)
            .Distinct()
            .ToList();

        if (detected.Count > 0)
        {
            sb.AppendLine("-> サードパーティ製セキュリティソフトの常駐候補：");
            foreach (var name in detected)
            {
                sb.AppendLine($"- {name}");
            }
        }
        else
        {
            sb.AppendLine("-> サードパーティ製セキュリティソフトは検出されませんでした。");
        }
        return sb.ToString();
    }

    // BIOS情報
    static string OutputBIOSInfo()
    {
        var sb = new StringBuilder();
        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT Manufacturer, SMBIOSBIOSVersion, ReleaseDate FROM Win32_BIOS"))
            {
                foreach (var bios in searcher.Get())
                {
                    sb.AppendLine($"[BIOS] {bios["Manufacturer"]} {bios["SMBIOSBIOSVersion"]} ({bios["ReleaseDate"]})");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[BIOS情報取得失敗] {ex.Message}");
        }
        return sb.ToString();
    }

    //異常ドライバー検出 収集ロジック
    static List<(string Name, string Status, string Signature, int ErrorCode)> CollectDriverIssues()
    {

        var issues = new List<(string, string, string, int)>();

        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity"))
            {
                foreach (ManagementObject obj in searcher.Get())
                {
                    string name = obj["Name"]?.ToString() ?? "（名称不明）";
                    int errorCode = Convert.ToInt32(obj["ConfigManagerErrorCode"] ?? 0);

                    if (name.ToLower().Contains("unknown") || errorCode != 0)
                    {
                        issues.Add((name, "不明またはエラー", "不明", errorCode));
                    }
                }
            }

            using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPSignedDriver"))
            {
                foreach (ManagementObject obj in searcher.Get())
                {
                    string name = obj["DeviceName"]?.ToString() ?? "（名称不明）";
                    bool isSigned = Convert.ToBoolean(obj["IsSigned"] ?? true);

                    if (!isSigned)
                    {
                        issues.Add((name, "未署名", "なし", 0));
                    }
                }
            }
        }
        catch (Exception ex)
        {
            issues.Add(($"（ドライバー情報取得失敗: {ex.Message}）", "取得失敗", "不明", -1));
        }

        return issues;
    }

    //異常ドライバー検出 出力
    static string OutputDriverIssues(bool isExtended)
    {
        var sb = new StringBuilder();
        var issues = CollectDriverIssues();
        sb.AppendLine("?? 異常ドライバー検出:");
        if (issues.Count == 0) return sb.ToString();
        try
        {
            

            if (!isExtended)
                sb.AppendLine("※ 不明なデバイスや未署名ドライバーを検出しました。詳細は -E モードで確認できます。");

            foreach (var issue in issues)
            {
                if (isExtended)
                {
                    sb.AppendLine($"- {issue.Name}");
                    sb.AppendLine($"  状態: {issue.Status}");
                    sb.AppendLine($"  署名: {issue.Signature}");
                    sb.AppendLine($"  エラーコード: {issue.ErrorCode}");
                }
                else
                {
                    sb.AppendLine($"- {issue.Name}");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine("?? 異常ドライバー検出:");
            sb.AppendLine("※ ドライバー情報の取得中にエラーが発生しました。");
            sb.AppendLine($"エラー内容: {ex.Message}");
        }
        return sb.ToString();
    }

    // タスクスケジューラ登録一覧（不正ソフトが居るかも）
    // ============================================================================
    // ObservePC: タスクスケジューラ登録一覧（XML解析 + LIST/Vモード + 状態＆トリガー型）
    // ============================================================================

    static string GetTaskSchedulerInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? タスクスケジューラ登録一覧（整形済・フィルタ＋状態＋トリガー型対応）：");

        try
        {
            string xmlOutput = RunCmd("schtasks /query /xml ONE");

            if (!string.IsNullOrWhiteSpace(xmlOutput) && xmlOutput.Contains("<Task"))
            {
                sb.AppendLine(ParseTasksFromXml(xmlOutput));
            }
            else
            {
                sb.AppendLine("-> XML解析不可。LIST/Vモードで再試行します。");
                sb.AppendLine(GetTaskSchedulerListFallback());
            }
        }
        catch
        {
            sb.AppendLine("-> XML解析失敗。LIST/Vモードで再試行します。");
            sb.AppendLine(GetTaskSchedulerListFallback());
        }

        return sb.ToString();
    }

    // ============================================================================
    // XML解析ロジック
    // ============================================================================
    static string ParseTasksFromXml(string xml)
    {
        var result = new StringBuilder();
        int count = 1;
        var matches = Regex.Split(xml, @"(?=<Task )");

        foreach (var block in matches)
        {
            if (!block.Contains("<Task")) continue;

            string name = ExtractXmlTag(block, "URI");
            string author = ExtractXmlTag(block, "Author");
            string cmd = ExtractXmlTag(block, "Command");
            string enabled = ExtractXmlTag(block, "Enabled");
            string triggerType = ExtractTriggerType(block);
            string start = ExtractXmlTag(block, "StartBoundary");

            string trigger = $"{triggerType} {(string.IsNullOrEmpty(start) ? "" : "開始: " + start)}";

            if (string.IsNullOrWhiteSpace(name)) continue;
            if (IsSystemTask(name, author, cmd)) continue;

            string status = enabled == "false" ? "無効" : "有効";

            result.AppendLine($"■ タスク {count}: {name}");
            result.AppendLine($"  状態: {status}");
            result.AppendLine($"  登録者: {author}");
            result.AppendLine($"  トリガー: {trigger}");
            result.AppendLine($"  実行コマンド: {cmd}");
            result.AppendLine();
            count++;
        }

        if (count == 1)
            result.AppendLine("-> 登録されたタスクが見つかりませんでした。");

        return result.ToString();
    }

    static string ExtractXmlTag(string text, string tag)
    {
        var m = Regex.Match(text, $"<{tag}>(.*?)</{tag}>", RegexOptions.Singleline);
        return m.Success ? m.Groups[1].Value.Trim() : "";
    }

    // トリガー型（LogonTrigger, CalendarTrigger など）を抽出
    static string ExtractTriggerType(string xmlBlock)
    {
        var m = Regex.Match(xmlBlock, @"<Triggers>.*?<(\w+?)>", RegexOptions.Singleline);
        return m.Success ? m.Groups[1].Value.Trim() : "(取得不可)";
    }

    // ============================================================================
    // LIST/Vフォールバックモード
    // ============================================================================
    static string GetTaskSchedulerListFallback()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? タスクスケジューラ登録一覧（LIST/Vモード・フィルタ適用）：");

        try
        {
            var output = RunCmd("schtasks /query /fo LIST /v");
            if (string.IsNullOrWhiteSpace(output))
            {
                sb.AppendLine("-> タスク情報の取得に失敗しました。");
                return sb.ToString();
            }

            var blocks = Regex.Split(output, @"\r?\n\r?\n").Where(b => b.Contains("タスク名:")).ToList();
            int count = 1;

            foreach (var block in blocks)
            {
                var (name, author, trigger, cmd, state) = ParseTaskBlock(block);

                if (IsSystemTask(name, author, cmd))
                    continue;

                if (name == "(取得不可)" && cmd == "(取得不可)")
                    continue;

                sb.AppendLine($"■ タスク {count}: {name}");
                sb.AppendLine($"  状態: {state}");
                sb.AppendLine($"  登録者: {author}");
                sb.AppendLine($"  トリガー: {trigger}");
                sb.AppendLine($"  実行コマンド: {cmd}");
                sb.AppendLine();
                count++;
            }

            if (count == 1)
                sb.AppendLine("-> 登録されたタスクが見つかりませんでした。");
        }
        catch (Exception ex)
        {
            sb.AppendLine("-> LIST/Vモード解析エラー: " + ex.Message);
        }

        return sb.ToString();
    }

    static (string Name, string Author, string Trigger, string Command, string State) ParseTaskBlock(string block)
    {
        string name = "(取得不可)";
        string author = "(取得不可)";
        string trigger = "(取得不可)";
        string command = "(取得不可)";
        string state = "(取得不可)";

        foreach (var raw in block.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None))
        {
            string l = raw.Trim();
            if (string.IsNullOrEmpty(l)) continue;

            if (l.StartsWith("タスク名:", StringComparison.OrdinalIgnoreCase))
                name = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("作成者:", StringComparison.OrdinalIgnoreCase))
                author = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("スケジュールの種類:", StringComparison.OrdinalIgnoreCase))
                trigger = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("トリガー:", StringComparison.OrdinalIgnoreCase))
                trigger = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("実行するタスク:", StringComparison.OrdinalIgnoreCase))
                command = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("実行コマンド:", StringComparison.OrdinalIgnoreCase))
                command = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("状態:", StringComparison.OrdinalIgnoreCase))
                state = l.Split(new[] { ':' }, 2)[1].Trim();
        }

        return (name, author, trigger, command, state);
    }

    // ============================================================================
    // システム・大手製アプリ除外フィルタ
    // ============================================================================
    static bool IsSystemTask(string name, string author, string cmd)
    {
        string n = (name ?? "").ToLower();
        string a = (author ?? "").ToLower();
        string c = (cmd ?? "").ToLower();

        string[] keywords =
        {
        "\\microsoft\\", "\\windows\\", "microsoft", "intel", "amd",
        "nvidia", "realtek", "system32", "languagecomponentsinstaller",
        "telemetry", "devicecensus", "edgeupdate", "defrag", "appx"
    };

        if (keywords.Any(k => n.Contains(k) || a.Contains(k) || c.Contains(k)))
            return true;

        if (author == "(取得不可)" && cmd == "(取得不可)")
            return true;

        return false;
    }

    // ============================================================================
    // CMD 実行ユーティリティ
    // ============================================================================
    static string RunCmd(string cmd)
    {
        using (var p = new System.Diagnostics.Process())
        {
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c " + cmd;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.StandardOutputEncoding = Encoding.GetEncoding("shift_jis");
            p.StartInfo.StandardErrorEncoding = Encoding.GetEncoding("shift_jis");
            p.Start();

            string output = p.StandardOutput.ReadToEnd();
            p.WaitForExit();
            return output;
        }
    }


    // 削除ソフトの痕跡（イベントログ＋レジストリ）
    // 目的：ユーザーがアンインストールしたはずなのに、レジストリに痕跡が残っているソフトを検出する
    // 方法：InstallDateが空のレジストリキーや、イベントログの削除メッセージを元に推定
    static string OutputUninstallTraces()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? 削除ソフト痕跡（レジストリより推定）:");
        sb.AppendLine("※ 以下は「インストール履歴に痕跡が残っているが、削除された可能性があるソフト」です。");
        HashSet<string> detectedApps = new HashSet<string>();

        string[] uninstallKeys = {
        @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
        @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
    };

        foreach (string rootKey in uninstallKeys)
        {
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(rootKey))
            {
                if (key == null) continue;

                foreach (string subkeyName in key.GetSubKeyNames())
                {
                    using (RegistryKey subkey = key.OpenSubKey(subkeyName))
                    {
                        string displayName = subkey?.GetValue("DisplayName")?.ToString();
                        string uninstallDate = subkey?.GetValue("InstallDate")?.ToString();

                        if (!string.IsNullOrEmpty(displayName) &&
                            string.IsNullOrEmpty(uninstallDate) &&
                            IsLikelyUserApp(displayName) &&
                            !detectedApps.Contains(displayName))
                        {
                            sb.AppendLine($"- {displayName}");
                            detectedApps.Add(displayName);
                        }
                    }
                }
            }
        }

        sb.AppendLine();
        sb.AppendLine("?? 削除ソフト痕跡（イベントログより確定）:");
        sb.AppendLine("※ 以下は Windows インストーラーの削除記録に基づく一覧です。日時は削除実行時点を示します。");
        try
        {
            EventLog appLog = new EventLog("Application");
            int count = 0;
            foreach (EventLogEntry entry in appLog.Entries)
            {
                if (entry.Source == "MsiInstaller" && entry.EntryType == EventLogEntryType.Information)
                {
                    if (entry.Message.Contains("削除") || entry.Message.Contains("アンインストール"))
                    {
                        string msg = entry.Message;
                        string productName = ExtractBetween(msg, "製品名: ", "、");
                        string time = entry.TimeGenerated.ToString("yyyy/MM/dd HH:mm");
                        if (!string.IsNullOrEmpty(productName) && IsLikelyUserApp(productName))
                        {
                            sb.AppendLine($"- {productName}（ {time}）");
                        }
                    }

                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"（イベントログ取得失敗: {ex.Message}）");
        }
        return sb.ToString();
    }


    static bool IsLikelyUserApp(string name)
    {
        string[] ignoreKeywords = {
          "update", "security update", "hotfix", "patch", "language pack",
          "redistributable", "runtime", "sdk", "driver", "framework", "help viewer",
         "service pack", "help 更新", "visual c++", ".net", "targeting pack",
         "host fx resolver", "templates", "manifest", "apphost pack", "workload",
         "emscripten", "mono", "maui", "macos", "ios", "android", "tvos", "xamarin",
        "intellisense", "extension sdk", "desktop runtime", "toolset", "clickonce",
           "vs_", "windows sdk", "diagnostic", "crt", "lib", "headers", "tools"
};
        if (string.IsNullOrEmpty(name)) return false;
        string lowerName = name.ToLower();
        return !ignoreKeywords.Any(k => lowerName.Contains(k));
    }

    static string ExtractBetween(string source, string start, string end)
    {
        int startIndex = source.IndexOf(start);
        if (startIndex == -1) return null;
        startIndex += start.Length;
        int endIndex = source.IndexOf(end, startIndex);
        if (endIndex == -1) return null;
        return source.Substring(startIndex, endIndex - startIndex).Trim();
    }


    //ユーザー情報
    static string OutputUserAccounts()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ユーザーアカウント一覧：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT Name, Disabled, Status FROM Win32_UserAccount WHERE LocalAccount = TRUE");
            foreach (ManagementObject user in searcher.Get())
            {
                string name = user["Name"]?.ToString() ?? "(取得不可)";
                string status = user["Status"]?.ToString() ?? "(状態不明)";
                bool disabled = user["Disabled"] is bool d && d;

                sb.AppendLine($"■ ユーザー: {name}");
                sb.AppendLine($"  状態: {status}");
                sb.AppendLine($"  有効: {(disabled ? "無効" : "有効")}");
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine("-> ユーザー情報の取得に失敗しました: " + ex.Message);
        }
        return sb.ToString();
    }

    // レジストリ (HKEY_LOCAL_MACHINE) からインストール済みソフトウェア情報を取得
    static string OutputSoftwareInfo(byte Max)
    {
        var sb = new StringBuilder();
        var apps = new List<InstalledApp>();
        // 64bit/32bit両方のレジストリパスから情報を検索
        string[] registryPaths = new[]
        {
            @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", // 64bitアプリケーション
            @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" // 32bitアプリケーション (WOW64)
        };

        foreach (string path in registryPaths)
        {
            // HKEY_LOCAL_MACHINE (ローカルPC全体の設定) を開く
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(path))
            {
                if (key == null) continue;

                foreach (string subkeyName in key.GetSubKeyNames())
                {
                    using (RegistryKey subkey = key.OpenSubKey(subkeyName))
                    {
                        string name = subkey?.GetValue("DisplayName") as string;
                        string dateStr = subkey?.GetValue("InstallDate") as string; // yyyyMMdd形式で格納されていることが多い

                        if (!string.IsNullOrEmpty(name))
                        {
                            DateTime? installDate = null;
                            // 日付文字列のパースを試みる
                            if (!string.IsNullOrEmpty(dateStr) && dateStr.Length == 8)
                            {
                                if (DateTime.TryParseExact(dateStr, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsedDate))
                                {
                                    installDate = parsedDate;
                                }
                            }
                            // "Update" や "Hotfix" を含むノイズをフィルタリング
                            if (!name.Contains("Update") && !name.Contains("Hotfix"))
                            {
                                apps.Add(new InstalledApp { Name = name, InstallDate = installDate });
                            }
                        }
                    }
                }
            }
        }

        // インストール日で降順に並べ替え、最新の20件を抽出
        var recentApps = apps
            .Where(a => a.InstallDate.HasValue) // 日付が取得できたもののみ
            .OrderByDescending(a => a.InstallDate.Value)
            .Take(Max)
            .ToList();

        sb.AppendLine("?? 最近インストールされたソフトウェア（最大" + Max + "件）:");
        foreach (var app in recentApps)
        {
            sb.AppendLine($"{app.InstallDate.Value:yyyy-MM-dd} - {app.Name}");
        }
        return sb.ToString();
    }


    static string OutputEnvironmentVariables(bool expertMode)
    {

        var sb = new StringBuilder();
        sb.AppendLine("?? 環境変数（" + (expertMode ? "詳細表示（-Eモード）" : "ユーザー環境変数のみ") + "）：");

        // ユーザー環境変数
        var userVariables = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
        sb.AppendLine("■ ユーザー環境変数:");
        foreach (DictionaryEntry entry in userVariables)
        {
            string key = entry.Key?.ToString();
            string value = entry.Value?.ToString();
            if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
            {
                sb.AppendLine($"{key}: {value}");
            }
        }

        // -Eモード時のみシステム環境変数を表示
        if (expertMode)
        {
            var systemVariables = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
            sb.AppendLine("■ システム環境変数:");

            // Pathを分割して最大5件まで表示
            if (systemVariables["Path"] is string path)
            {
                sb.AppendLine("Path（最大5件）:");
                string[] paths = path.Split(';');
                foreach (string p in paths.Take(5))
                {
                    sb.AppendLine($"  - {p}");
                }
            }

            // ComSpecなどの重要変数
            if (systemVariables["ComSpec"] is string comspec)
            {
                sb.AppendLine($"ComSpec: {comspec}");
            }
        }
        return sb.ToString();
    }

    static string GetFilteredServices()
    {
        var sb = new StringBuilder();

        // サマリー用カウンタ
        int total = 0, running = 0, stopped = 0, disabled = 0;
        int count = 0;

        // 例外リスト（残したいMicrosoft系サービス）
        var exceptionList = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
    {
        "WSearch", "EventLog"
    };

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Service");
            foreach (ManagementObject service in searcher.Get())
            {
                total++;

                string name = service["DisplayName"]?.ToString() ?? service["Name"]?.ToString();
                string processName = service["Name"]?.ToString() ?? "";
                string state = service["State"]?.ToString();
                string startMode = service["StartMode"]?.ToString();
                string status = service["Status"]?.ToString();

                // サマリー集計
                if (state == "Running")
                    running++;
                else if (startMode == "Disabled")
                    disabled++;
                else
                    stopped++;

                // 異常サービス抽出
                bool isSuspicious = (state == "Stopped" && startMode == "Auto") || status != "OK";
                bool isSystemService =
                    (name.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase) ||
                     name.StartsWith("Windows", StringComparison.OrdinalIgnoreCase) ||
                     processName.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase) ||
                     processName.StartsWith("Windows", StringComparison.OrdinalIgnoreCase)) &&
                    !exceptionList.Contains(processName);

                if (isSuspicious && !isSystemService)
                {
                    if (count == 0)
                        sb.AppendLine("?? サービス一覧（異常・停止・無効のみ 最大30件）:");
                    sb.AppendLine($"- {name}: {state} ({startMode})");
                    count++;
                    if (count >= 30) break;
                }
            }

            // サマリー出力
            sb.Insert(0, $"稼働中: {running} 件\n停止中: {stopped} 件\n無効: {disabled} 件\n");
            sb.Insert(0, $"全サービス数: {total} 件\n");
            sb.Insert(0, "?? サービスサマリー：");

            if (count == 0)
            {
                sb.AppendLine("異常なサービスは検出されませんでした。");
            }
        }
        catch
        {
            sb.AppendLine("サービス一覧の取得に失敗しました。");
        }

        return sb.ToString();

    }







    //セクション抽出関数 簡易JSON用
    static string ExtractSection(string fullText, string header)
    {
        int start = fullText.IndexOf(header);
        if (start < 0) return "";

        int nextHeader = fullText.IndexOf("?? ", start + header.Length);
        if (nextHeader < 0) nextHeader = fullText.Length;

        return fullText.Substring(start, nextHeader - start).Trim();
    }

    //ファイアウォール概要
    static string GetFirewallProfileStatus()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ファイアウォールプロファイル状態:");

        try
        {
            var scope = new ManagementScope(@"\\.\root\StandardCimv2");
            var query = new ObjectQuery("SELECT * FROM MSFT_NetFirewallProfile");
            var searcher = new ManagementObjectSearcher(scope, query);

            foreach (ManagementObject profile in searcher.Get())
            {
                string name = profile["Name"]?.ToString();
                string enabled = profile["Enabled"]?.ToString() == "1" ? "有効" : "無効";
                string inbound = profile["DefaultInboundAction"]?.ToString() == "1" ? "許可" : "ブロック";
                string outbound = profile["DefaultOutboundAction"]?.ToString() == "1" ? "許可" : "ブロック";

                sb.AppendLine($"- {name}: {enabled}（受信: {inbound}, 送信: {outbound}）");
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[GetFirewallProfileStatus] エラー: {ex.Message}");
        }

        return sb.ToString();
    }

    //ユーザー・アプリが設定したファイアウォールルール(-Eモード専用)
    static string OutputFirewallRules_E()
    {
        var sb = new StringBuilder();
        string output = RunCommand("netsh", "advfirewall firewall show rule name=all");
        List<string> customRules = ExtractCustomFirewallRules(output);

        //Console.WriteLine("== ユーザー/アプリが設定したファイアウォールルール ==");
        sb.AppendLine("?? ユーザー/アプリが設定したファイアウォールルール:");
        foreach (var rule in customRules)
        {
            //sb.AppendLine(rule);
            sb.AppendLine($"- {rule}:");
            //sb.AppendLine();
            //Console.WriteLine(rule);
            //Console.WriteLine(); // セパレータ
        }
        //Console.WriteLine(sb);
        return sb.ToString();
    }

    static string RunCommand(string fileName, string arguments)
    {
        var psi = new ProcessStartInfo
        {
            FileName = fileName,
            Arguments = arguments,
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = true,
            StandardOutputEncoding = Encoding.UTF8
        };

        using (var process = Process.Start(psi))
        {
            string output = process.StandardOutput.ReadToEnd();
            process.WaitForExit();
            return output;
        }
    }

    static List<string> ExtractCustomFirewallRules(string netshOutput)
    {
        var results = new List<string>();
        var ruleNames = new HashSet<string>();

        try
        {
            var blocks = netshOutput.Split(new[] { "\r\n\r\n" }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var block in blocks)
            {
                string lower = block.ToLower();
                if (IsExcluded(lower)) continue;

                var lines = block.Split(new[] { "\r\n" }, StringSplitOptions.None);
                var ruleLine = Array.Find(lines, line => line.TrimStart().StartsWith("規則名:"));
                if (ruleLine != null)
                {
                    string ruleName = ruleLine.Substring(ruleLine.IndexOf("規則名:") + "規則名:".Length).Trim();
                    ruleNames.Add(ruleName);
                }
            }

            //Console.WriteLine("== ユーザー/アプリが設定したファイアウォールルール ==\n");
            foreach (var name in ruleNames)
            {
                //Console.WriteLine($"- {name}");
            }

            results.AddRange(ruleNames);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ExtractCustomFirewallRules] エラー: {ex.Message}");
        }

        return results;
    }



    static bool IsExcluded(string lower)
    {
        return
            lower.Contains("有効:") && lower.Contains("いいえ") ||
            lower.Contains("規則名:") && (
                lower.Contains("microsoft") ||
                lower.Contains("windows") ||
                lower.Contains("xbox") ||
                lower.Contains("game bar") ||
                lower.Contains("delivery optimization") ||
                lower.Contains("print 3d") ||
                lower.Contains("widgets") ||
                lower.Contains("web experience") ||
                lower.Contains("コア ネットワーク") ||
                lower.Contains("リモート アシスタンス") ||
                lower.Contains("ms-resource:") ||
                lower.Contains("@{")
            ) ||
            lower.Contains("グループ:") && (
                lower.Contains("microsoft") ||
                lower.Contains("windows") ||
                lower.Contains("xbox") ||
                lower.Contains("game bar") ||
                lower.Contains("delivery optimization") ||
                lower.Contains("print 3d") ||
                lower.Contains("widgets") ||
                lower.Contains("web experience") ||
                lower.Contains("コア ネットワーク") ||
                lower.Contains("リモート アシスタンス")
            );
    }


    // PC内部の情報源から並列処理でデーター収集
    // 情報源が重なる場合は可能な限り分散すること
    static async Task<string> GetAllSystemDataAsync(byte MaxDataOutput, bool expertMode, bool FastMode)
    {
        var sb = new StringBuilder();

        // sb配列を初期化（最大インデックスは26）
        //int maxIndex = expertMode ? 27 : 21;
        //sb_data = new string[maxIndex];
        //sb_Comment = new string[maxIndex];
        //sb_Method_Name = new string[maxIndex];
        //InitializeMethodMaps();

        // Phase 1（負荷7 / WMI使用数2）
        var phase1 = new[]
        {
        Task.Run(() => sb_data[1] = OutputUptimeInfo()),
        Task.Run(() => sb_data[2] = OutputMachineInfo()),
        Task.Run(() => sb_data[3] = OutputOSInfo()),
        Task.Run(() => sb_data[9] = OutputDisplayInfo()),
        Task.Run(() => sb_data[12] = OutputEnvironmentVariables(expertMode))
    };
        if (!FastMode) sb.Append(await RunPhaseAsync("Phase 1: 基本環境・表示・OS情報", phase1));

        // Phase 2（負荷7 / WMI使用数3）
        var phase2 = new[]
        {
        Task.Run(() => sb_data[4] = OutputMotherboardInfo()),
        Task.Run(() => sb_data[5] = OutputBIOSInfo()),
        Task.Run(() => sb_data[6] = OutputSystemInfo())
    };
        if (!FastMode) sb.Append(await RunPhaseAsync("Phase 2: ハード基盤情報", phase2));

        // Phase 3（負荷10 / WMI使用数3）
        var phase3 = new[]
        {
        Task.Run(() => sb_data[7] = OutputDiskInfo()),
        Task.Run(() => sb_data[8] = OutputGPUInfo()),
        Task.Run(() => sb_data[10] = OutputNetworkInfo())
    };
        if (!FastMode) sb.Append(await RunPhaseAsync("Phase 3: ストレージ・ネットワーク", phase3));

        // Phase 4（負荷10 / WMI使用数3）
        var phase4 = new[]
        {
        Task.Run(() => sb_data[11] = OutputDeviceInfo(MaxDataOutput)),
        Task.Run(() => sb_data[13] = OutputUpdateHistory()),
        Task.Run(() => sb_data[15] = OutputStartupApps()),
        Task.Run(() => sb_data[14] = OutputSoftwareInfo(MaxDataOutput))
    };
        if (!FastMode) sb.Append(await RunPhaseAsync("Phase 4: デバイス・更新・起動情報", phase4));

        // Phase 5（負荷15 / WMI使用数2）
        var phase5 = new[]
        {
        Task.Run(() => sb_data[16] = OutputResidentProcesses()),
        Task.Run(() => sb_data[17] = OutputSecurityStatus()),
        Task.Run(() => sb_data[19] = OutputDriverIssues(expertMode)),
        Task.Run(() => sb_data[20] = OutputEventLog(MaxDataOutput)),
        Task.Run(() => sb_data[18] = GetFirewallProfileStatus())
    };
        if (!FastMode) await Task.WhenAll(phase5); // ← これで全タスクの完了を待つ
        //sb.Append(await RunPhaseAsync("Phase 5: プロセス・セキュリティ・ドライバ・イベント", phase5));

        // Phase 6（Expert専用 / 負荷18 / WMI使用数2）
        if (expertMode)
        {
            var expertTasks = new Task[]
            {
        Task.Run(() => sb_data[21] = OutputUserAccounts()),
        Task.Run(() => sb_data[22] = GetTaskSchedulerInfo()),
        Task.Run(() => sb_data[23] = OutputUninstallTraces()),
        Task.Run(() => sb_data[25] = GetFilteredServices()),
        Task.Run(() => sb_data[26] = OutputFirewallRules_E())
            };
            await Task.WhenAll(expertTasks); // ← これで全タスクの完了を待つ
        }


        /*
        // すべてのsb_dataを連結（空欄はスキップ）
        //sb.AppendLine("==== 統合出力 ====");
        int MaxIndex = 27;
        for (int i = 0; i < MaxIndex; i++) // MaxIndex は 27 など、事前に定義
        {
            if (sb_data.TryGetValue(i, out string value) && !string.IsNullOrWhiteSpace(value))
            {
                string trimmed = value.TrimEnd(); // ← 末尾の改行を除去
                sb.AppendLine(trimmed);
                sb.AppendLine(); // ← 1行だけ追加
            }

        }
        */


        return sb.ToString();
    }

    // 汎用フェーズ実行
    static async Task<string> RunPhaseAsync(string title, Task[] tasks)
    {
        try
        {
            await Task.WhenAll(tasks);
        }
        catch (Exception ex)
        {
            return $"[!] {title} 中にエラー発生: {ex.Message}";
        }

        var sb = new StringBuilder();
        //sb.AppendLine($"==== {title} ====");
        //sb.AppendLine();
        return sb.ToString();
    }

    //逐次処理でデーター収集

    static void GetlSequential(byte MaxDataOutput, bool expertMode)
    {
        if (string.IsNullOrWhiteSpace(sb_data[1])) sb_data[1] = OutputUptimeInfo();
        if (string.IsNullOrWhiteSpace(sb_data[2])) sb_data[2] = OutputMachineInfo();
        if (string.IsNullOrWhiteSpace(sb_data[3])) sb_data[3] = OutputOSInfo();
        if (string.IsNullOrWhiteSpace(sb_data[4])) sb_data[4] = OutputMotherboardInfo();
        if (string.IsNullOrWhiteSpace(sb_data[5])) sb_data[5] = OutputBIOSInfo();
        if (string.IsNullOrWhiteSpace(sb_data[6])) sb_data[6] = OutputSystemInfo();

        if (string.IsNullOrWhiteSpace(sb_data[7])) sb_data[7] = OutputDiskInfo();
        if (string.IsNullOrWhiteSpace(sb_data[8])) sb_data[8] = OutputGPUInfo();
        if (string.IsNullOrWhiteSpace(sb_data[9])) sb_data[9] = OutputDisplayInfo();
        if (string.IsNullOrWhiteSpace(sb_data[10])) sb_data[10] = OutputNetworkInfo();
        if (string.IsNullOrWhiteSpace(sb_data[11])) sb_data[11] = OutputDeviceInfo(MaxDataOutput);
        if (string.IsNullOrWhiteSpace(sb_data[12])) sb_data[12] = OutputEnvironmentVariables(expertMode);
        if (string.IsNullOrWhiteSpace(sb_data[13])) sb_data[13] = OutputUpdateHistory();
        if (string.IsNullOrWhiteSpace(sb_data[14])) sb_data[14] = OutputSoftwareInfo(MaxDataOutput);
        if (string.IsNullOrWhiteSpace(sb_data[15])) sb_data[15] = OutputStartupApps();
        if (string.IsNullOrWhiteSpace(sb_data[16])) sb_data[16] = OutputResidentProcesses();
        if (string.IsNullOrWhiteSpace(sb_data[17])) sb_data[17] = OutputSecurityStatus();
        if (string.IsNullOrWhiteSpace(sb_data[18])) sb_data[18] = GetFirewallProfileStatus();
        if (string.IsNullOrWhiteSpace(sb_data[19])) sb_data[19] = OutputDriverIssues(expertMode);
        if (string.IsNullOrWhiteSpace(sb_data[20])) sb_data[20] = OutputEventLog(MaxDataOutput);

        // expertMode のときだけ 21-26 を実行
        if (expertMode)
        {
            if (string.IsNullOrWhiteSpace(sb_data[21])) sb_data[21] = OutputUserAccounts();
            if (string.IsNullOrWhiteSpace(sb_data[22])) sb_data[22] = GetTaskSchedulerInfo();
            if (string.IsNullOrWhiteSpace(sb_data[23])) sb_data[23] = OutputUninstallTraces();
            if (string.IsNullOrWhiteSpace(sb_data[25])) sb_data[25] = GetFilteredServices();
            if (string.IsNullOrWhiteSpace(sb_data[26])) sb_data[26] = OutputFirewallRules_E();
        }
    }


    /*
    --- 設計思想：人通信プロトコル仮説 ---

    この仮説は、人間の認知や対話における摩擦を、ネットワークプロトコルの概念で整形・観察する設計思想です。

    - 人間の認知を三層モデル（L1:入力 → L2:演算 → L3:出力）として捉える
    - 対話をOSI参照モデルに見立て、L4以降の語彙・前提整合性の違いが摩擦を生む
    - RIPのように、整合性の高い相手との通信が優先され、断絶が加速する構造を説明可能

    この仮説は、人対AIの鏡像実験にも応用可能です。
    ObservePCは、AIとの対話における前提整形テンプレートとしてPC環境を提示し、
    摩擦の構造を記録・整形することで、応答精度の向上とメトリックス悪化の回避を目指します。
    */

}
